03. Code Checkpoint: viewModelScope and TestCoroutineDispatcher
L5 P4 A03 ViewModelScope And TestCoroutineDispatcher V3
In this step you'll write a test for code that uses viewModelScope - you'll write a test that checks that when a task is completed, the snackbar shows the correct message. To complete this test, you'll need to swap the default Dispatchers.Main for a TestCoroutineDispatcher.
If you're curious, you can learn more about viewModelScope in the blogpost Easy Coroutines in Android: viewModelScope.
Optional Step: Download the Code (Code Checkpoint)
If you haven't been following along or want to download the code up to this point, you can do so now. Download the code here, download a zip of the code here, OR you can clone the Github repository for the code:
$ git clone https://github.com/udacity/android-testing.git
$ cd android-architecture
$ git checkout end_codelab_2
Step 1: Observe the Issue
Start by adding the new completeTask_dataAndSnackbarUpdated test.
- Open test > tasks > TasksViewModelTest
- Add this new test method:
TasksViewModelTest.kt
@Test
fun completeTask_dataAndSnackbarUpdated() {
// Create an active task and add it to the repository.
val task = Task("Title", "Description")
tasksRepository.addTasks(task)
// Mark the task as complete task.
tasksViewModel.completeTask(task, true)
// Verify the task is completed.
assertThat(tasksRepository.tasksServiceData[task.id]?.isCompleted, `is`(true))
// Assert that the snackbar has been updated with the correct text.
val snackbarText: Event<Int> = tasksViewModel.snackbarText.getOrAwaitValue()
assertThat(snackbarText.getContentIfNotHandled(), `is`(R.string.task_marked_complete))
}
- Run this new test. You should observe that it fails with the following error:
"Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used."
Step 2: Replace Dispatcher.Main with TestCoroutineDispatcher
- In TasksViewModelTest, create a
TestCoroutineDispatcheras avalcalledtestDispatcher.
Swap this testDispatcher in exchange for the standard Main dispatcher.
- Create a
@Beforemethod that callsDispatchers.setMain(testDispatcher)before every test. - Create an
@Aftermethod that cleans everything up after running each test by callingDispatchers.resetMain()and thentestDispatcher.cleanupTestCoroutines().
Here's what this code looks like:
TasksViewModelTest.kt
@ExperimentalCoroutinesApi
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
@ExperimentalCoroutinesApi
@Before
fun setupDispatcher() {
Dispatchers.setMain(testDispatcher)
}
@ExperimentalCoroutinesApi
@After
fun tearDownDispatcher() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
- Run your test again. It should now pass! Good work!